package ga;

import java.io.IOException;
import java.io.InputStream;


public class AnalizadorLexicoLengDescAlgoritmica 
{
	enum EstadoLexico
	{
		INICIAL, ES_INICIO_ASIGNACION , ES_ASIGNACION, ES_COMENTARIO,ES_NUMERO, ES_IDENTIFICADOR, 
		ES_MAS, ES_MENOS, ES_POR,ES_ENTRE,ES_FLECHA,ES_PARENT_INICIAL,ES_PARENT_FINAL,
		ES_CORCHETE_INICIAL,ES_CORCHETE_FINAL,ES_EOF,ES_AMPERSAND,ES_PUNTOYCOMA, 
		//DUDA
		ES_CCORCHETE,   
		
		ES_COMA,ES_PUNTO,ES_PUNTERO,ES_LLAVE_INICIAL,ES_LLAVE_FINAL
	};
	
	private final static int EOF = -1;
	private int caract;
	private EstadoLexico estado;
	private InputStream input;
	private String lexema;
	private int fila;
	private int col;
	private int filaInicio;
    private int colInicio;
	
	
	
	public AnalizadorLexicoLengDescAlgoritmica(InputStream input) throws IOException
	{
		this.input = input;
		col = -1;
		fila = 0;
//DUDA: esto va con la duda de abajo. Antes ponia sigCaracter()
		caract = sigCar();
	}
	
//DUDA: esto creo que hay que eliminarlo, porque tienen dos metodos sigCaracter y otro sigCar() que hacen lo mismo
	/*private int sigCaracter() throws IOException
	{
		caract = input.read();
		if(caract == '\n')
		{
			col = -1;
			fila++;
		}
		else
			col++;
		
		return caract;
	}*/
	
	public Token sigToken() throws IOException 
	{
        estado = EstadoLexico.INICIAL;
        filaInicio = fila;
        colInicio = col;
        lexema = "";
        while (true) 
		{
        	System.out.println("Carac: " + (char) caract + " Estado: "
					+ estado.toString() + " Lexema: " + lexema);
            switch (estado) 
			{
                case INICIAL:
                	if (caract == '_' || esLetra(caract)){
						transita(EstadoLexico.ES_IDENTIFICADOR);
					}else if (esDigito(caract)){
                        transita(EstadoLexico.ES_NUMERO);
                    }else if (caract == ':'){
						transita(EstadoLexico.ES_INICIO_ASIGNACION);
					} else if (caract == '['){
                        transita(EstadoLexico.ES_CORCHETE_INICIAL);
					}else if (caract == ']'){
//DUDA no deberia ser es_corchete_final??? es que hay DOS!!
                        transita(EstadoLexico.ES_CCORCHETE);
                    }else if (caract == '('){
						transita(EstadoLexico.ES_PARENT_INICIAL);
					}else if (caract == ')'){
						transita(EstadoLexico.ES_PARENT_FINAL);	
					} else if (caract == '+'){
                        transita(EstadoLexico.ES_MAS);
                    } else if (caract == '-'){
                        transita(EstadoLexico.ES_MENOS);
					}else if (caract == '&'){
                        transita(EstadoLexico.ES_AMPERSAND);
                    }else if (caract == '*'){
                        transita(EstadoLexico.ES_POR);
                    } else if (caract == '/'){
                        transita(EstadoLexico.ES_ENTRE);
                    }else if (caract == '@'){
						transitaIgnorando(EstadoLexico.ES_COMENTARIO);
                    }else if (caract == EOF){
                        transita(EstadoLexico.ES_EOF);
                    }else if (caract == ';'){
						transita(EstadoLexico.ES_PUNTOYCOMA);
					} else if (caract == ','){
                        transita(EstadoLexico.ES_COMA);
                    }else if (caract == '.'){
                        transita(EstadoLexico.ES_PUNTO);
                    } else if (caract == '^'){
                        transita(EstadoLexico.ES_PUNTERO);
					} else if (caract == '{'){
                        transitaIgnorando(EstadoLexico.ES_LLAVE_INICIAL);
                    } else if (caract == '}'){
                        transitaIgnorando(EstadoLexico.ES_LLAVE_FINAL);
                    }else if (esSep(caract)){
                        transitaIgnorando(EstadoLexico.INICIAL);
                    }
                    else {
                        errorLexico();
                    }
                    break;
                
                case ES_INICIO_ASIGNACION:
					if (caract == '=') {
						transita(EstadoLexico.ES_ASIGNACION);
						break;
					}
                case ES_ASIGNACION:
					return new Token(filaInicio,colInicio,CatLexica.ASIGNACION);
                case ES_CORCHETE_INICIAL:
					if(caract == ']')
						transita(EstadoLexico.ES_CORCHETE_FINAL);
					else 
						return new Token(filaInicio,colInicio,CatLexica.CORCHETEAPERTURA);
					break;
				case ES_CORCHETE_FINAL:
                    return new Token(filaInicio,colInicio,CatLexica.CORCHETES);
//DUDA:este el el segundo corchete de cierre!!
				case ES_CCORCHETE:
                    return new Token(filaInicio,colInicio,CatLexica.CORCHETECIERRE);
				case ES_PARENT_INICIAL:
                	return new Token(filaInicio,colInicio,CatLexica.PARENTESISAPERTURA);
                case ES_PARENT_FINAL:
                	return new Token(filaInicio,colInicio,CatLexica.PARENTESISCIERRE);
                case ES_LLAVE_INICIAL:
                	return new Token(filaInicio,colInicio,CatLexica.LLAVEAPERTURA);
                case ES_LLAVE_FINAL:
                	return new Token(filaInicio,colInicio,CatLexica.LLAVECIERRE);
                case ES_MAS:
                    return new Token(filaInicio,colInicio,CatLexica.MAS);
                case ES_MENOS:
					if(caract == '>')
						transita(EstadoLexico.ES_FLECHA);
					else return new Token(filaInicio,colInicio,CatLexica.MENOS);
					break;
                case ES_POR:
                    return new Token(filaInicio,colInicio,CatLexica.POR);
				case ES_ENTRE:
                    return new Token(filaInicio,colInicio,CatLexica.ENTRE);
                case ES_AMPERSAND:
                    return new Token(filaInicio,colInicio,CatLexica.AMPERSAND);
                case ES_PUNTOYCOMA:
                    return new Token(filaInicio,colInicio,CatLexica.PUNTOCOMA);
				case ES_FLECHA:
					return new Token(filaInicio,colInicio,CatLexica.FLECHA);
				case ES_NUMERO:
                    if (esDigito(caract)) {
                        transita(EstadoLexico.ES_NUMERO);
                    } else {
                        return new Token(filaInicio,colInicio,CatLexica.NUMERO, lexema);
                    }
                    break;
                case ES_IDENTIFICADOR:
                	if (esLetra(caract) || esDigito(caract) || caract == '_')
                	{
                		transita(EstadoLexico.ES_IDENTIFICADOR);
                	} else {
                		return tokenSimbolo();
                	}
                	break;
                case ES_EOF:
                    return new Token(filaInicio,colInicio,CatLexica.EOF);
                case ES_COMENTARIO:
                    if (caract != '\n' && caract != EOF) {
                        transitaIgnorando(EstadoLexico.ES_COMENTARIO);
                    } else {
                        transitaIgnorando(EstadoLexico.INICIAL);
                    }
                    break;                
                case ES_COMA:
                    return new Token(filaInicio,colInicio,CatLexica.COMA);    
                case ES_PUNTO:
                    return new Token(filaInicio,colInicio,CatLexica.PUNTO);    
                case ES_PUNTERO:
                    return new Token(filaInicio,colInicio,CatLexica.PUNTERO);  
                
            }
        }
    }
	
	
	private void transita(EstadoLexico aEstado) throws IOException 
	{
        lexema = lexema + (char) caract;
        transitaIgnorando(aEstado);
    }

    private void transitaIgnorando(EstadoLexico aEstado) throws IOException 
	{
        estado = aEstado;
        filaInicio = fila;
        colInicio = col;
        caract = sigCar();
    }

    private int sigCar() throws IOException 
	{
        caract = input.read();
        if (caract == '\n') {
            col = -1;
            fila++;
        } else {
            col++;
        }
        return caract;
    }

    private boolean esLetra(int car) {
        return (car >= 'a' && car <= 'z') || (car >= 'A' && car <= 'Z');
    }

    private boolean esDigito(int car) {
        return (car >= '0' && car <= '9');
    }

    private boolean esSep(int car) {
        return car == ' ' || car == '\t' || car == '\n' || car == '\r' || car == '\b';
    }

    private void errorLexico() {
        System.err.println("(" + fila + "," + col + ")" + 
                "ERROR LEXICO: caracter desconocido:" + (char) caract);
        System.exit(1);
    }

    ;
   private Token tokenSimbolo() {
        if (lexema.equals("true"))
            return new Token(filaInicio,colInicio,CatLexica.TRUE);
        else if (lexema.equals("false")) 
            return new Token(filaInicio,colInicio,CatLexica.FALSE);
        else if (lexema.equals("and")) 
            return new Token(filaInicio,colInicio,CatLexica.AND);
        else if (lexema.equals("or")) 
            return new Token(filaInicio,colInicio,CatLexica.OR);
        else if (lexema.equals("not")) 
            return new Token(filaInicio,colInicio,CatLexica.NOT);
        else if (lexema.equals("eq")) 
            return new Token(filaInicio,colInicio,CatLexica.EQ);
        else if (lexema.equals("neq")) 
            return new Token(filaInicio,colInicio,CatLexica.NEQ);
        else if (lexema.equals("lt")) 
            return new Token(filaInicio,colInicio,CatLexica.LT);
        else if (lexema.equals("le")) 
            return new Token(filaInicio,colInicio,CatLexica.LE);
        else if (lexema.equals("gt")) 
            return new Token(filaInicio,colInicio,CatLexica.GT);
        else if (lexema.equals("ge")) 
            return new Token(filaInicio,colInicio,CatLexica.GE);
        else if (lexema.equals("if")) 
            return new Token(filaInicio,colInicio,CatLexica.IF);
        else if (lexema.equals("fi")) 
            return new Token(filaInicio,colInicio,CatLexica.FI);
        else if (lexema.equals("do")) 
            return new Token(filaInicio,colInicio,CatLexica.DO);
        else if (lexema.equals("od")) 
            return new Token(filaInicio,colInicio,CatLexica.OD);
        else if (lexema.equals("case")) 
            return new Token(filaInicio,colInicio,CatLexica.CASE);
        else if (lexema.equals("int")) 
            return new Token(filaInicio,colInicio,CatLexica.INT);
        else if (lexema.equals("boolean")) 
            return new Token(filaInicio,colInicio,CatLexica.BOOLEAN);
        else if (lexema.equals("proc")) 
            return new Token(filaInicio,colInicio,CatLexica.PROCEDIMIENTO);
        else if (lexema.equals("var")) 
            return new Token(filaInicio,colInicio,CatLexica.VARIABLE);
        else if (lexema.equals("tipo")) 
            return new Token(filaInicio,colInicio,CatLexica.TIPO);
        else if (lexema.equals("registro")) 
            return new Token(filaInicio,colInicio,CatLexica.REGISTRO);
        else if (lexema.equals("puntero")) 
            return new Token(filaInicio,colInicio,CatLexica.PUNTEROTIPO);
        else if (lexema.equals("de")) 
            return new Token(filaInicio,colInicio,CatLexica.DE);
        else if (lexema.equals("tabla")) 
            return new Token(filaInicio,colInicio,CatLexica.TABLA);
        else if (lexema.equals("reserva")) 
            return new Token(filaInicio,colInicio,CatLexica.NEW);
        else if (lexema.equals("libera")) 
            return new Token(filaInicio,colInicio,CatLexica.DELETE);
        else if (lexema.equals("leer")) 
            return new Token(filaInicio,colInicio,CatLexica.LECTURA);
        else if (lexema.equals("escribir")) 
            return new Token(filaInicio,colInicio,CatLexica.ESCRITURA);
        
        else
            return new Token(filaInicio,colInicio,CatLexica.IDENTIFICADOR, lexema);
    }

}
